/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.biz.modular.goods.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.core.util.StpClientUtil;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.biz.modular.apilog.entity.DevApiLog;
import vip.xiaonuo.biz.modular.apilog.mapper.DevApiLogMapper;
import vip.xiaonuo.biz.modular.cabinetpermission.service.BizCabinetPermissionService;
import vip.xiaonuo.biz.modular.goods.entity.*;
import vip.xiaonuo.biz.modular.goods.enums.BizGoodsEnum;
import vip.xiaonuo.biz.modular.goods.mapper.BizGoodsAdminMapper;
import vip.xiaonuo.biz.modular.goods.mapper.BizGoodsCheckMapper;
import vip.xiaonuo.biz.modular.goods.mapper.BizGoodsLabelMapper;
import vip.xiaonuo.biz.modular.goods.mapper.BizGoodsMapper;
import vip.xiaonuo.biz.modular.goods.param.ClientGoodsQryParam;
import vip.xiaonuo.biz.modular.goods.param.GoodsInfoParam;
import vip.xiaonuo.biz.modular.goods.param.GoodsQryDto;
import vip.xiaonuo.biz.modular.goods.param.GoodsScrapParam;
import vip.xiaonuo.biz.modular.goods.result.*;
import vip.xiaonuo.biz.modular.goods.service.BizGoodsLedgerService;
import vip.xiaonuo.biz.modular.goods.service.BizGoodsService;
import vip.xiaonuo.biz.modular.relation.service.BizRelationService;
import vip.xiaonuo.biz.modular.user.entity.BizUser;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.page.CommonPageRequest;
import vip.xiaonuo.common.pojo.CommonFile;
import vip.xiaonuo.common.util.CommonDownloadUtil;
import vip.xiaonuo.dev.api.DevConfigApi;
import vip.xiaonuo.dev.api.DevDictApi;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 物品Service接口实现类
 *
 * @author 古月墨塵
 * @date 2022/11/18 13:44
 **/
@Service
@Transactional
public class BizGoodsServiceImpl extends ServiceImpl<BizGoodsMapper, BizGoods> implements BizGoodsService {

    @Resource
    private BizGoodsAdminMapper goodsAdminMapper;

    @Resource
    private BizGoodsLabelMapper goodsLabelMapper;

    @Resource
    private BizGoodsCheckMapper goodsCheckMapper;

    @Resource
    private BizRelationService bizRelationService;

    @Resource
    private BizGoodsLedgerService bizGoodsLedgerService;

    @Resource
    private BizCabinetPermissionService bizCabinetPermissionService;

    @Resource
    private BizGoodsMapper bizGoodsMapper;

    @Resource
    private DevConfigApi devConfigApi;

    @Resource
    private DevDictApi devDictApi;

    @Resource
    private DevApiLogMapper devApiLogMapper;

    @Override
    public ClientGoodsSummaryResult queryGoodsAdminSummary(Integer goodsType) {
        String userId = StpClientUtil.getLoginIdAsString();
        long cabinetTotalQuantity = getBaseMapper().countCabinetDoorByAdmin(userId, goodsType); // 柜门总数
        long goodsTotalQuantity = getBaseMapper().countGoodsByAdminId(userId, null, null, goodsType); // 物品总数
        long goodsMaintainingQuantity = getBaseMapper().countGoodsByAdminId(userId, null, 2, goodsType); // 维修中总数
        long goodsOutOfCabinetQuantity = getBaseMapper().countGoodsByAdminId(userId, 1, 1, goodsType); // 离柜总数
        long goodsScrapQuantity = getBaseMapper().countGoodsByAdminId(userId, 0, 0, goodsType); // 报废总数
        long goodsInCabinetQuantity = getBaseMapper().countInGoodsByAdminId(userId, goodsType); // 在柜总数
        // 全部柜子
        return ClientGoodsSummaryResult.builder()
                .cabinetTotalQuantity(cabinetTotalQuantity)
                .goodsTotalQuantity(goodsTotalQuantity)
                .goodsInCabinetQuantity(goodsInCabinetQuantity)
                .goodsMaintainingQuantity(goodsMaintainingQuantity)
                .goodsOutOfCabinetQuantity(goodsOutOfCabinetQuantity)
                .goodsScrapQuantity(goodsScrapQuantity)
                .build();
    }

    @Override
    public ClientGoodsSummaryResult queryGoodsUserSummary(Integer goodsType) {
        long goodsInCabinetQuantity = bizGoodsMapper.selectCount(new LambdaQueryWrapper<BizGoods>()
                .eq(BizGoods::getLeaveFlag, 0)
                .eq(BizGoods::getGoodsType, goodsType)
                .eq(BizGoods::getState, 1) // 报废的和维修的不算
                .eq(BizGoods::getDeleteFlag, "NOT_DELETE")); // 在柜数量

        long goodsMaintainingQuantity = bizGoodsMapper.selectCount(new LambdaQueryWrapper<BizGoods>()
                .eq(BizGoods::getState, 2)
                .eq(BizGoods::getGoodsType, goodsType)
                .eq(BizGoods::getDeleteFlag, "NOT_DELETE")); // 维修数量

        long goodsOutOfCabinetQuantity = bizGoodsMapper.selectCount(new LambdaQueryWrapper<BizGoods>()
                .eq(BizGoods::getLeaveFlag, 1)
                .ne(BizGoods::getState, 0) // 离柜数量不应该包含报废的（bug-285，测试原话：离柜统计数据应该是未报废、离柜的数据）
                .eq(BizGoods::getGoodsType, goodsType)
                .eq(BizGoods::getDeleteFlag, "NOT_DELETE")); // 离柜数量

        long goodsScrapQuantity = bizGoodsMapper.selectCount(new LambdaQueryWrapper<BizGoods>()
                .eq(BizGoods::getState, 0)
                .ne(BizGoods::getLeaveFlag, 1) // 离柜的不算报废
                .eq(BizGoods::getGoodsType, goodsType)
                .eq(BizGoods::getDeleteFlag, "NOT_DELETE")); //报废数量
        // 全部柜子
        return ClientGoodsSummaryResult.builder()
                .goodsInCabinetQuantity(goodsInCabinetQuantity)
                .goodsMaintainingQuantity(goodsMaintainingQuantity)
                .goodsOutOfCabinetQuantity(goodsOutOfCabinetQuantity)
                .goodsScrapQuantity(goodsScrapQuantity)
                .build();
    }

    @Override
    public void handleReagentFistAvlData(BizGoods bizGoods, String reagentWeight) {
        BizGoods goods = new BizGoods();
        //容器重量
        goods.setReagentContainerWeight(NumberUtil.sub(reagentWeight, bizGoods.getReagentInitWeight().toString()));
        goods.setId(bizGoods.getId());
        bizGoodsMapper.updateById(goods);
    }

    @Override
    public void handleReagentAvlData(BizGoods bizGoods, String lastWeight) {
        BizGoods goods = new BizGoods();
        goods.setReagentAvlWeight(new BigDecimal(lastWeight));
        if (bizGoods.getReagentDensity() != null) {
            goods.setReagentAvlVolume(NumberUtil.div(lastWeight, bizGoods.getReagentDensity().toString(), 2));
        }
        goods.setId(bizGoods.getId());
        bizGoodsMapper.updateById(goods);
    }

    @Override
    public GoodsPageResult detailForScan(GoodsQryDto dto) {
        GoodsPageResult result = new GoodsPageResult();
        if (StringUtils.isBlank(dto.getScanCode()) || dto.getGoodsType() == null) {
            throw new CommonException("无法识别的扫码操作！");
        }
        String qrCode = dto.getScanCode();
        if (qrCode.indexOf("_") > 0) {
            qrCode = qrCode.substring(0, qrCode.indexOf("_"));
        }
        //查询物品信息
        GoodsPageResult info = this.baseMapper.getInfoByQrCode(qrCode);
        if (info == null) {
            //场景-无记录
            String emptyMsg = "未找到对应的物品信息！";
            if (dto.getGoodsType() == 1) {
                emptyMsg = emptyMsg.replace("物品", "试剂");
            }
            throw new CommonException(emptyMsg);
        } else {
            //场景-扫码入库错误（例：物品扫码入库扫到了试剂）
            if (!dto.getGoodsType().equals(info.getGoodsType())) {
                String emptyMsg = "未找到对应的物品信息！";
                if (dto.getGoodsType() == 1) {
                    emptyMsg = emptyMsg.replace("物品", "试剂");
                }
                throw new CommonException(emptyMsg);
            }
        }
        if (info.getLeaveFlag() == 0 || info.getEnterFlag() == 1) {
            //场景-已入库
            String leaveMsg = "当前物品已入库！";
            if (dto.getGoodsType() == 1) {
                leaveMsg = leaveMsg.replace("物品", "试剂");
            }
            throw new CommonException(leaveMsg);
        }
        if (info.getSsbrFlag() == 0) {
            //场景-不支持扫码操作
            String limitMsg = "当前物品不支持扫码操作！";
            if (dto.getGoodsType() == 1) {
                limitMsg = limitMsg.replace("物品", "试剂");
            }
            throw new CommonException(limitMsg);
        }
        if (StringUtils.isNotBlank(info.getGoodsImg())) {
            String baseUrl = devConfigApi.getValueByKey("BACKEND_BASE_URL");
            info.setGoodsImg(getImageUrl(info.getGoodsImg(), baseUrl));
        }
        result = info;
        return result;
    }

    @Override
    public Page<GoodsPageResult> page(GoodsQryDto dto) {
        //当前用户
        if (!StpUtil.hasRoleOr("superAdmin", "sysAdmin")) {
            SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
            dto.setUserId(saBaseLoginUser.getId());
        }
        return this.baseMapper.getPageList(CommonPageRequest.defaultPageForDto(dto), dto);
    }

    @Override
    public void add(GoodsInfoParam param) {
        //校验重复编号
        List<BizGoods> list = this.getByCode(param.getGoodsCode(), null);
        if (!CollectionUtils.isEmpty(list)) {
            throw new CommonException("编号已存在，请重新输入！");
        }
        if (param.getRfidFlag() == 1) {
            //校验重复rfid
            List<BizGoods> rfidList = this.getByRfid(param.getRfidNum(), null);
            if (!CollectionUtils.isEmpty(rfidList)) {
                throw new CommonException("RFID已存在，请重新输入！");
            }
        }
        BizGoods base = BeanUtil.toBean(param, BizGoods.class);
        if (param.getQrcodeFlag() == 1 || param.getBarcodeFlag() == 1) {
            base.setQrCode(this.getQrCode());
        }
        //试剂计算
        if (base.getGoodsType() == 1) {
            if (base.getReagentDensity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new CommonException("试剂密度不可以≤ 0 ！");
            }
            //ml
            if (base.getReagentUnit().equals("0")) {
                base.setReagentInitVolume(base.getReagentInitNetAmount());
                base.setReagentInitWeight(NumberUtil.mul(base.getReagentInitNetAmount(), base.getReagentDensity()));
            }
            //g
            if (base.getReagentUnit().equals("1")) {
                base.setReagentInitVolume(NumberUtil.div(base.getReagentInitNetAmount(), base.getReagentDensity(), 2));
                base.setReagentInitWeight(base.getReagentInitNetAmount());
            }
            //可用 = 初始净量
            base.setReagentAvlWeight(base.getReagentInitWeight());
            base.setReagentAvlVolume(base.getReagentInitVolume());
        }
        //保存
        this.save(base);
        //处理额外数据
        this.handleExtraData(param, base.getId());
    }

    @Override
    public GoodsInfoResult detail(GoodsQryDto dto) {
        GoodsInfoResult result = new GoodsInfoResult();
        BizGoods goods = this.baseMapper.selectById(dto.getGoodsId());
        result = BeanUtil.toBean(goods, GoodsInfoResult.class);
        //管理员
        List<BizUser> adminList = goodsAdminMapper.getListByGoodsId(dto.getGoodsId());
        result.setAdminList(adminList);
        //标签
        List<String> labels = goodsLabelMapper.getListByGoodsId(dto.getGoodsId());
        result.setLabels(labels);
        //检查项
        List<BizGoodsCheck> checkItems = goodsCheckMapper.getAllListByGoodsId(dto.getGoodsId());
        result.setCheckItems(checkItems);
        return result;
    }

    @Override
    public void edit(GoodsInfoParam param) {
        BizGoods base = this.baseMapper.selectById(param.getId());
        if (param.getRfidFlag() == 1) {
            //校验重复rfid
            List<BizGoods> rfidList = this.getByRfid(param.getRfidNum(), param.getId());
            if (!CollectionUtils.isEmpty(rfidList)) {
                throw new CommonException("RFID已存在，请重新输入！");
            }
        }
        BizGoods goods = BeanUtil.toBean(param, BizGoods.class);
        if (base.getUnknownFlag() == 1) {
            goods.setUnknownFlag(0);
        }
        if ((param.getQrcodeFlag() == 1 || param.getBarcodeFlag() == 1) && StringUtils.isBlank(base.getQrCode())) {
            //选了二维码or条形码 且快捷码空时
            goods.setQrCode(this.getQrCode());
        }
        //未知物品 且 试剂
        if (base.getUnknownFlag() == 1 && base.getGoodsType() == 1) {
            if (base.getReagentDensity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new CommonException("试剂密度不可以≤ 0 ！");
            }
            //ml
            if (goods.getReagentUnit().equals("0")) {
                goods.setReagentInitVolume(goods.getReagentInitNetAmount());
                goods.setReagentInitWeight(NumberUtil.mul(goods.getReagentInitNetAmount(), goods.getReagentDensity()));
            }
            //g
            if (goods.getReagentUnit().equals("1")) {
                goods.setReagentInitVolume(NumberUtil.div(goods.getReagentInitNetAmount(), goods.getReagentDensity(), 2));
                goods.setReagentInitWeight(goods.getReagentInitNetAmount());
            }
            //可用 = 初始净量
            base.setReagentAvlWeight(base.getReagentInitWeight());
            base.setReagentAvlVolume(base.getReagentInitVolume());
            //计算容器重量
            if (base.getReagentFirstWeigh() != null && base.getReagentContainerWeight() == null) {
                if (NumberUtil.sub(base.getReagentFirstWeigh(), goods.getReagentInitWeight()).compareTo(BigDecimal.ZERO) <= 0) {
                    String messgae = "试剂\"{0}\"初始净量为{1}g，大于首次称重{2}g，请检查试剂的初始净量";
                    throw new CommonException(MessageFormat.format(messgae, goods.getGoodsName(), goods.getReagentInitWeight(), base.getReagentFirstWeigh()));
                }
                goods.setReagentContainerWeight(NumberUtil.sub(base.getReagentFirstWeigh(), goods.getReagentInitWeight()));
            }
        }
        if (StringUtils.isBlank(param.getBioCabinetGroupId()) || StringUtils.isBlank(param.getBioCabinetDoorId()) || StringUtils.isBlank(param.getBioCabinetId())) {
            goods.setBioCabinetGroupId("");
            goods.setBioCabinetDoorId("");
            goods.setBioCabinetId("");
        }
        //处理几个空字符串
        if (StringUtils.isBlank(param.getAssetCode())) {
            goods.setAssetCode("");
        }
        if (StringUtils.isBlank(param.getGoodsModel())) {
            goods.setGoodsModel("");
        }
        if (StringUtils.isBlank(param.getGoodsMfr())) {
            goods.setGoodsMfr("");
        }
        //更新
        this.updateById(goods);
        //处理额外数据
        this.handleExtraData(param, base.getId());
    }

    @Override
    public void changeCabinet(List<GoodsInfoParam> goodsInfoParamList) {
        this.baseMapper.changeCabinet(goodsInfoParamList);
    }

    @Override
    public void delete(GoodsInfoParam param) {
        BizGoods base = this.baseMapper.selectById(param.getId());
        if (base.getState() != 0) {
            throw new CommonException("非报废状态无法删除");
        }
        this.removeById(param.getId());
    }

    @Override
    public void enable(GoodsInfoParam param) {
        BizGoods base = this.baseMapper.selectById(param.getId());
        if (base.getState() != 2) {
            throw new CommonException("非维修状态无法启用");
        }
        BizGoods goods = new BizGoods();
        goods.setId(param.getId());
        goods.setState(1);
        this.updateById(goods);
        //处理台账
        this.handleLedger(base, 1);
    }

    @Override
    public void scrap(GoodsScrapParam param) {
        BizGoods base = this.baseMapper.selectById(param.getGoodsId());
        if (base == null) {
            throw new CommonException("信息查询异常");
        }
        if (base.getState() == 0) {
            throw new CommonException("已为报废状态");
        }
        BizGoods goods = new BizGoods();
        goods.setId(param.getGoodsId());
        goods.setState(0);
        goods.setScrapReason(param.getScrapReason());
        this.updateById(goods);
        //保存附件
        bizRelationService.saveRelationBatchWithClear(param.getGoodsId(), param.getFileIds(), BizGoodsEnum.GOODS_SCRAP.getValue());
        //处理台账
        this.handleLedger(base, 3);
    }

    @Override
    public GoodsScrapResult getScrapInfo(GoodsQryDto dto) {
        GoodsScrapResult result = new GoodsScrapResult();
        BizGoods base = this.baseMapper.selectById(dto.getGoodsId());
        if (base == null) {
            throw new CommonException("信息查询异常");
        }
        result = BeanUtil.toBean(base, GoodsScrapResult.class);
        List<CommonFile> fileList = this.baseMapper.getScrapFileList(dto.getGoodsId());
        result.setFileList(fileList);
        return result;
    }

    @Override
    public void repair(GoodsInfoParam param) {
        BizGoods base = this.baseMapper.selectById(param.getId());
        if (base == null) {
            throw new CommonException("信息查询异常");
        }
        if (base.getState() != 1) {
            throw new CommonException("非启用状态无法申请维修");
        }
        BizGoods goods = new BizGoods();
        goods.setId(param.getId());
        goods.setState(2);
        this.updateById(goods);
        //处理台账
        this.handleLedger(base, 2);
    }

    /**
     * 处理额外数据
     *
     * @param param
     * @param goodsId
     */
    private void handleExtraData(GoodsInfoParam param, String goodsId) {
        //处理管理员
        List<String> adminList = new ArrayList<>();
        if (StringUtils.isNotBlank(param.getAdminUserIds())) {
            //清除管理员
            goodsAdminMapper.removeByGoodsId(goodsId);
            adminList = Arrays.asList(param.getAdminUserIds().split(","));
        }
        //兼容导入的
        if (StringUtils.isBlank(param.getCreateUser())) {
            //当前用户
            SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
            if (CollectionUtils.isEmpty(adminList) && saBaseLoginUser.getRoleCodeList().contains("cabAdmin")) {
                //是柜子管理员且为空清空下 否则谁选的默认是谁
                adminList.add(saBaseLoginUser.getId());
            }
        }

        for (String adminId : adminList) {
            BizGoodsAdmin goodsAdmin = new BizGoodsAdmin();
            goodsAdmin.setId(null);
            goodsAdmin.setGoodsId(goodsId);
            goodsAdmin.setUserId(adminId);
            goodsAdminMapper.insert(goodsAdmin);
        }
        //处理标签
        if (!CollectionUtils.isEmpty(param.getLabels())) {
            //清除标签
            goodsLabelMapper.removeByGoodsId(goodsId);
            for (String label : param.getLabels()) {
                BizGoodsLabel goodsLabel = new BizGoodsLabel();
                goodsLabel.setId(null);
                goodsLabel.setGoodsId(goodsId);
                goodsLabel.setLabelId(label);
                goodsLabelMapper.insert(goodsLabel);
            }
        }
        //处理检查项
        if (!CollectionUtils.isEmpty(param.getCheckItems())) {
            //清除检查项
            goodsCheckMapper.removeByGoodsId(goodsId);
            for (BizGoodsCheck goodsCheck : param.getCheckItems()) {
                goodsCheck.setId(null);
                goodsCheck.setGoodsId(goodsId);
                goodsCheckMapper.insert(goodsCheck);
            }
        }
    }


    /**
     * 根据编号获取lsit 校验用
     *
     * @param code
     * @param id
     * @return
     */
    private List<BizGoods> getByCode(String code, String id) {
        if (StringUtils.isNotBlank(code)) {
            QueryWrapper<BizGoods> queryWrapper = new QueryWrapper<>();
            if (StringUtils.isNotBlank(id)) {
                queryWrapper.lambda().ne(BizGoods::getId, id);
            }
            queryWrapper.lambda().eq(BizGoods::getGoodsCode, code);
            return this.list(queryWrapper);
        } else {
            return null;
        }
    }

    private List<BizGoods> getByRfid(String rfid, String id) {
        QueryWrapper<BizGoods> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(id)) {
            queryWrapper.lambda().ne(BizGoods::getId, id);
        }
        queryWrapper.lambda().eq(BizGoods::getRfidNum, rfid);
        return this.list(queryWrapper);
    }

    /**
     * 生成12位的快捷码
     *
     * @return
     */
    private String getQrCode() {
        String result = RandomUtil.randomNumbers(12);
        QueryWrapper<BizGoods> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(BizGoods::getQrCode, result);
        List<BizGoods> list = this.baseMapper.selectList(queryWrapper);
        //一直判定到唯一为止
        while (!CollectionUtils.isEmpty(list)) {
            result = RandomUtil.randomNumbers(12);
            queryWrapper.lambda().eq(BizGoods::getQrCode, result);
            list = this.baseMapper.selectList(queryWrapper);
        }
        return result;
    }

    /**
     * 处理台账（普通）
     *
     * @param goods
     * @param ledgerType
     */
    @Override
    public void handleLedger(BizGoods goods, Integer ledgerType) {
        BizGoodsLedger entity = new BizGoodsLedger();
        entity.setLedgerType(ledgerType);
        entity.setGoodsId(goods.getId());
        entity.setCurCabinetGroupId(goods.getCurCabinetGroupId());
        entity.setCurCabinetId(goods.getCurCabinetId());
        entity.setCurCabinetDoorId(goods.getCurCabinetDoorId());
        bizGoodsLedgerService.save(entity);
    }

    @Override
    public void handleLedgerForEventOfGoodsInCab(BizGoods goods, DevApiLog apiLog) {
        BizGoodsLedger entity = new BizGoodsLedger();
        entity.setLedgerType(0);
        entity.setGoodsId(goods.getId());
        entity.setCurCabinetGroupId(goods.getCurCabinetGroupId());
        entity.setCurCabinetId(goods.getCurCabinetId());
        entity.setCurCabinetDoorId(goods.getCurCabinetDoorId());
        entity.setApiLogId(apiLog.getId());
        entity.setRetApiId(apiLog.getId());
        entity.setRetCabinetGroupId(goods.getCurCabinetGroupId());
        entity.setRetCabinetId(goods.getCurCabinetId());
        entity.setRetCabinetDoorId(goods.getCurCabinetDoorId());
        entity.setWarehouseUserId(apiLog.getUserId());
        entity.setWarehouseTime(apiLog.getEventTime());
        entity.setCreateUser(apiLog.getUserId());
        if (goods.getGoodsType() == 1) {
            //试剂首次入库，取入库前最后一次称重的信息。记录归还重量
            if (StringUtils.isNotBlank(goods.getRfidNum())) {
                DevApiLog lastWeighInfo = devApiLogMapper.getLastOneForReagentWeigh(goods.getRfidNum(), apiLog.getEventTime());
                if (lastWeighInfo != null) {
                    entity.setReagentRetApiId(lastWeighInfo.getId());
                    entity.setReagentRetWeight(lastWeighInfo.getReagentWeight());
                    if (goods.getReagentDensity() != null) {
                        entity.setReagentRetVolume(NumberUtil.div(lastWeighInfo.getReagentWeight(), goods.getReagentDensity().toString(), 2).toString());
                    }
                }
            }
        }
        bizGoodsLedgerService.save(entity);
    }

    @Override
    public Date getReturnTime(BizGoods goods, Date borTime) {
        Date result = DateUtil.date();
        //偏移单位 默认：分钟
        DateField offsetUnit = DateField.MINUTE;
        if (null != goods.getMaxLimitLeaveUnit()) {
            if (goods.getMaxLimitLeaveUnit() == 2) {
                offsetUnit = DateField.HOUR_OF_DAY;
            }
            if (goods.getMaxLimitLeaveUnit() == 3) {
                offsetUnit = DateField.DAY_OF_MONTH;
            }
        }
        if (goods.getMaxLimitLeave() == null) {
            result = DateUtil.parse("9999-12-31 23:59:59");
        } else {
            result = DateUtil.offset(borTime, offsetUnit, goods.getMaxLimitLeave());
        }
        return result;
    }

    @Override
    public void exportGoods(GoodsQryDto dto, HttpServletResponse response) {
        File tempFile = null;
        try {
            //当前用户
            if (!StpUtil.hasRoleOr("superAdmin", "sysAdmin")) {
                SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
                dto.setUserId(saBaseLoginUser.getId());
            }

            List<GoodsExportResult> exportList = new ArrayList<>();

            List<GoodsPageResult> list = this.baseMapper.getResultList(dto);
            for (GoodsPageResult item : list) {
                GoodsExportResult goodsExportResult = new GoodsExportResult();
                BeanUtil.copyProperties(item, goodsExportResult);
                if (item.getUnknownFlag() == 1) {
                    goodsExportResult.setGoodsName("未知");
                }
                goodsExportResult.setStoCabDoor(StrUtil.nullToEmpty(item.getBioCabinetName()) + "-" + StrUtil.nullToEmpty(item.getBioCabinetDoorName()));
                goodsExportResult.setStoPlace(item.getBioCabinetLoationName());
                goodsExportResult.setCurCabDoor(StrUtil.nullToEmpty(item.getCurCabinetName()) + "-" + StrUtil.nullToEmpty(item.getCurCabinetDoorName()));
                goodsExportResult.setCurPlace(item.getCurCabinetLoationName());
                goodsExportResult.setInCabStauts(this.getGoodsLeaveStauts(item.getLeaveFlag()));
                List<String> idMethodList = new ArrayList<>();
                if (item.getRfidFlag() == 1) {
                    idMethodList.add("RFID");
                }
                if (item.getQrcodeFlag() == 1) {
                    idMethodList.add("二维码");
                }
                if (item.getBarcodeFlag() == 1) {
                    idMethodList.add("条形码");
                }
                if (!CollectionUtils.isEmpty(idMethodList)) {
                    goodsExportResult.setIdMethods(String.join("/", idMethodList));
                }
                goodsExportResult.setGoodsStauts(this.getGoodsStauts(item.getState()));
                //RFID+二维码+条形码部分
                if (item.getRfidFlag() == 1) {
                    goodsExportResult.setRfidNum(item.getRfidNum());
                } else {
                    goodsExportResult.setRfidNum(null);
                }
                if (item.getQrcodeFlag() == 1) {
                    goodsExportResult.setQrCode(item.getQrCode() + "_" + item.getGoodsName());
                } else {
                    goodsExportResult.setQrCode(null);
                }
                if (item.getBarcodeFlag() == 1) {
                    goodsExportResult.setBarCode(item.getQrCode());
                } else {
                    goodsExportResult.setBarCode(null);
                }
                exportList.add(goodsExportResult);
            }
            String fileName = "物品清单";
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(fileName, "物品列表"),
                    GoodsExportResult.class, exportList);
            tempFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName + ".xls");
            BufferedOutputStream outputStream = FileUtil.getOutputStream(tempFile);
            workbook.write(outputStream);
            outputStream.close();
            CommonDownloadUtil.download(tempFile, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException("物品导出失败");
        } finally {
            FileUtil.del(tempFile);
        }
    }

    @Override
    public void exportReagent(GoodsQryDto dto, HttpServletResponse response) {
        File tempFile = null;
        try {
            //当前用户
            if (!StpUtil.hasRoleOr("superAdmin", "sysAdmin")) {
                SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
                dto.setUserId(saBaseLoginUser.getId());
            }

            List<GoodsExportResult> exportList = new ArrayList<>();

            List<GoodsPageResult> list = this.baseMapper.getResultList(dto);
            for (GoodsPageResult item : list) {
                GoodsExportResult goodsExportResult = new GoodsExportResult();
                BeanUtil.copyProperties(item, goodsExportResult);
                if (item.getUnknownFlag() == 1) {
                    goodsExportResult.setGoodsName("未知");
                }
                goodsExportResult.setStoCabDoor(StrUtil.nullToEmpty(item.getBioCabinetName()) + "-" + StrUtil.nullToEmpty(item.getBioCabinetDoorName()));
                goodsExportResult.setStoPlace(item.getBioCabinetLoationName());
                goodsExportResult.setCurCabDoor(StrUtil.nullToEmpty(item.getCurCabinetName()) + "-" + StrUtil.nullToEmpty(item.getCurCabinetDoorName()));
                goodsExportResult.setCurPlace(item.getCurCabinetLoationName());
                goodsExportResult.setInCabStauts(this.getGoodsLeaveStauts(item.getLeaveFlag()));
                List<String> idMethodList = new ArrayList<>();
                //RFID+二维码+条形码部分
                if (item.getRfidFlag() == 1) {
                    idMethodList.add("RFID");
                }
                if (item.getQrcodeFlag() == 1) {
                    idMethodList.add("二维码");
                }
                if (item.getBarcodeFlag() == 1) {
                    idMethodList.add("条形码");
                }
                if (!CollectionUtils.isEmpty(idMethodList)) {
                    goodsExportResult.setIdMethods(String.join("/", idMethodList));
                }
                goodsExportResult.setGoodsStauts(this.getGoodsStauts(item.getState()));
                //RFID+二维码+条形码部分
                if (item.getRfidFlag() == 1) {
                    goodsExportResult.setRfidNum(item.getRfidNum());
                } else {
                    goodsExportResult.setRfidNum(null);
                }
                if (item.getQrcodeFlag() == 1) {
                    goodsExportResult.setQrCode(item.getQrCode() + "_" + item.getGoodsName());
                } else {
                    goodsExportResult.setQrCode(null);
                }
                if (item.getBarcodeFlag() == 1) {
                    goodsExportResult.setBarCode(item.getQrCode());
                } else {
                    goodsExportResult.setBarCode(null);
                }
                exportList.add(goodsExportResult);
            }
            String fileName = "试剂清单";
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(fileName, "试剂列表"),
                    GoodsExportResult.class, exportList);
            tempFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName + ".xls");
            BufferedOutputStream outputStream = FileUtil.getOutputStream(tempFile);
            workbook.write(outputStream);
            outputStream.close();
            CommonDownloadUtil.download(tempFile, response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException("试剂导出失败");
        } finally {
            FileUtil.del(tempFile);
        }
    }

    @Override
    public Page<ClientGoodsPageResult> pageForClient(ClientGoodsQryParam param) {
        String userId = StpClientUtil.getLoginIdAsString();
        param.setUserId(userId);
        Page<ClientGoodsPageResult> page = getBaseMapper().pageForClient(Page.of(param.getCurrent(), param.getSize()), param);
        List<ClientGoodsPageResult> records = page.getRecords();
        String baseUrl = devConfigApi.getValueByKey("BACKEND_BASE_URL");
        if (ObjectUtil.isEmpty(baseUrl)) throw new CommonException("服务端URL未正确配置");

        // 1，状态是“启用”的仪器，显示”是否在柜“为“在柜”和“离柜”；状态是“维修中”和”已报废“的直接显示其状态
        records.forEach(record -> {
            //设置图片
            record.setGoodsImg(getImageUrl(record.getGoodsImg(), baseUrl));
            record.setStateText(getStateText(record.getState(), record.getLeaveFlag()));
            // 状态 0-已报废 1-启用 2-维修中，状态是“启用”的仪器，根据用户对其存放柜门如果有权限，则显示“拥有借用权”
            if (record.getState() == 1) {
                // 设置权限
                boolean hasPerm = bizCabinetPermissionService.hasDoorPermWithCurrentUser(false,
                        record.getBioCabinetDoorId(),
                        record.getBioCabinetId(),
                        record.getBioCabinetGroupId(), userId);
                record.setHasPerm(hasPerm);
            }
        });
        return page;
    }

    private String getImageUrl(String fileId, String baseUrl) {
        if (StringUtils.isEmpty(fileId)) return "";
        return baseUrl + "/dev/file/download?id=" + fileId;
    }

    @Override
    public ClientGoodsDetailResult getDetailForClient(String goodsId, String qrCode) {
        String baseUrl = devConfigApi.getValueByKey("BACKEND_BASE_URL");
        if (ObjectUtil.isEmpty(baseUrl)) throw new CommonException("服务端URL未正确配置");

        String userId = StpClientUtil.getLoginIdAsString();
        ClientGoodsDetailResult record = getBaseMapper().getDetailForClient(goodsId, userId, qrCode);
        Assert.notNull(record, "记录不存在");
        record.setStateText(getStateText(record.getState(), record.getLeaveFlag()));
        // 状态 0-已报废 1-启用 2-维修中，状态是“启用”的仪器，根据用户对其存放柜门如果有权限，则显示“拥有借用权”
        if (record.getState() == 1) {
            // 设置权限
            boolean hasPerm = bizCabinetPermissionService.hasDoorPermWithCurrentUser(false,
                    record.getBioCabinetDoorId(),
                    record.getBioCabinetId(),
                    record.getBioCabinetGroupId(), userId);
            record.setHasPerm(hasPerm);
        }
        // 添加物品管理员
        record.setAdminList(goodsAdminMapper.getListByGoodsId(record.getGoodsId()).stream().map(BizUser::getName).
                collect(Collectors.toList()));
        // 添加检查项
        record.setCheckItems(goodsCheckMapper.getListByGoodsId(record.getGoodsId()).stream().map(item -> {
            ClientGoodsCheckResult check = new ClientGoodsCheckResult();
            BeanUtil.copyProperties(item, check);
            return check;
        }).collect(Collectors.toList()));
        record.setGoodsImg(getImageUrl(record.getGoodsImg(), baseUrl));
        if (record.getGoodsType() == 1) {
            // 填充试剂类型
            if (StringUtils.isNotEmpty(record.getReagentClassId())) {
                String name = devDictApi.getLabelByCode(record.getReagentClassId());
                record.setReagentClassName(name);
            }
            //试剂形态
            if (StringUtils.isNotEmpty(record.getReagentFormId())) {
                String name = devDictApi.getLabelByCode(record.getReagentFormId());
                record.setReagentFormName(name);
            }
            //试剂形态
            if (StringUtils.isNotEmpty(record.getReagentRedoxClassId())) {
                String name = devDictApi.getLabelByCode(record.getReagentRedoxClassId());
                record.setReagentRedoxClassName(name);
            }
            //试剂瓶类型
            if (StringUtils.isNotEmpty(record.getReagentBottleType())) {
                String name = devDictApi.getLabelByCode(record.getReagentBottleType());
                record.setReagentBottleTypeName(name);
            }
        }
        return record;
    }


    private String getStateText(Integer state, Integer leaveFlag) {
        //状态 0-已报废 1-启用 2-维修中
        if (state == 0) return "已报废";
        if (state == 1) {
            // 离柜标识 0-否 1-是
            if (leaveFlag == 0) return "在柜";
            if (leaveFlag == 1) return "离柜";
        }
        if (state == 2) return "维修中";
        return "";
    }

    private String getGoodsStauts(Integer state) {
        switch (state) {
            case 0:
                return "已报废";
            case 1:
                return "启用";
            case 2:
                return "维修中";
            default:
                return "未知";
        }
    }

    private String getGoodsLeaveStauts(Integer state) {
        switch (state) {
            case 0:
                return "在柜";
            case 1:
                return "离柜";
            default:
                return "未知";
        }
    }
}
